import pandas as pd
import numpy as np
import statsmodels.api as sm
from statsmodels.formula.api import ols
import tkinter as tk
from tkinter import ttk, messagebox
from tkinter import filedialog
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import scipy.stats as stats
from mpl_toolkits.mplot3d import Axes3D
from scipy.optimize import minimize
import sympy as sp  # 符号计算库
import re

# 设置matplotlib支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示为方块的问题


class DOEAnalyzer:
    def __init__(self, parent):
        self.root = tk.Frame(parent)
        self.root.pack(fill=tk.BOTH, expand=True)

        self.csv_path = None

        # 创建主框架
        self.main_frame = tk.Frame(self.root)
        self.main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 文件选择区域
        self.file_frame = tk.Frame(self.main_frame)
        self.file_frame.pack(fill=tk.X, pady=10)

        self.file_path_var = tk.StringVar()
        self.file_entry = tk.Entry(self.file_frame, textvariable=self.file_path_var, width=40)
        self.file_entry.pack(side=tk.LEFT, padx=5)

        self.select_button = tk.Button(self.file_frame, text="选择文件", command=self.select_file)
        self.select_button.pack(side=tk.LEFT, padx=5)

        self.analyze_button = tk.Button(self.file_frame, text="执行分析", command=self.perform_analysis,
                                        state=tk.DISABLED)
        self.analyze_button.pack(side=tk.LEFT, padx=5)

        # 优化目标和方法选择区域
        self.optimization_frame = tk.Frame(self.main_frame)
        self.optimization_frame.pack(fill=tk.X, pady=5)

        # 优化目标选择
        self.objective_label = tk.Label(self.optimization_frame, text="优化目标:")
        self.objective_label.pack(side=tk.LEFT, padx=5)

        self.objective_var = tk.StringVar(value="最大化")  # 默认最大化
        self.max_radio = tk.Radiobutton(self.optimization_frame, text="最大化", variable=self.objective_var,
                                        value="最大化", command=self._on_objective_change)
        self.min_radio = tk.Radiobutton(self.optimization_frame, text="最小化", variable=self.objective_var,
                                        value="最小化", command=self._on_objective_change)
        self.max_radio.pack(side=tk.LEFT, padx=5)
        self.min_radio.pack(side=tk.LEFT, padx=5)

        # 优化方法选择
        self.method_label = tk.Label(self.optimization_frame, text="优化方法:")
        self.method_label.pack(side=tk.LEFT, padx=5)

        self.method_var = tk.StringVar(value="解析法")
        self.numerical_radio = tk.Radiobutton(self.optimization_frame, text="数值法", variable=self.method_var,
                                              value="数值法", command=self._on_method_change)
        self.analytical_radio = tk.Radiobutton(self.optimization_frame, text="解析法", variable=self.method_var,
                                               value="解析法", command=self._on_method_change)
        self.numerical_radio.pack(side=tk.LEFT, padx=5)
        self.analytical_radio.pack(side=tk.LEFT, padx=5)

        self.optimize_button = tk.Button(self.optimization_frame, text="执行优化",
                                         command=self.optimize, state=tk.DISABLED)
        self.optimize_button.pack(side=tk.LEFT, padx=10)

        # 结果展示区域 - 使用Notebook进行分类展示
        self.notebook = ttk.Notebook(self.main_frame)
        self.notebook.pack(fill=tk.BOTH, expand=True, pady=10)

        # 创建各个标签页
        self.summary_tab = tk.Frame(self.notebook)
        self.effects_tab = tk.Frame(self.notebook)
        self.surface_tab = tk.Frame(self.notebook)
        self.gradient_tab = tk.Frame(self.notebook)  # 优化结果标签页
        self.model_tab = tk.Frame(self.notebook)  # 模型展示标签页

        self.notebook.add(self.summary_tab, text="分析摘要")
        self.notebook.add(self.effects_tab, text="效应分析")
        self.notebook.add(self.surface_tab, text="响应曲面")
        self.notebook.add(self.gradient_tab, text="优化结果")  # 优化结果标签页
        self.notebook.add(self.model_tab, text="模型展示")  # 模型展示标签页

        # 创建摘要区域的文本框
        self.summary_text = tk.Text(self.summary_tab, wrap=tk.WORD, height=20, width=80)
        self.summary_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 优化结果的文本框
        self.gradient_text = tk.Text(self.gradient_tab, wrap=tk.WORD, height=20, width=80)
        self.gradient_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 模型展示的文本框
        self.model_text = tk.Text(self.model_tab, wrap=tk.WORD, height=20, width=80)
        self.model_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 初始化图表变量
        self.goodness_of_fit_fig = None
        self.effects_fig = None
        self.surface_fig = None
        self.optimization_fig = None  # 优化结果图表

        # 存储因素信息
        self.factors_info = None
        self.model = None  # 存储模型
        self.response = None  # 存储响应变量名
        self.quadratic_model = None  # 存储二次模型
        self.quadratic_formula = None  # 存储二次模型公式
        self.sympy_model = None  # 存储符号模型
        self.sympy_vars = None  # 存储符号变量
        self.optimization_type = "最大化"  # 优化目标，默认最大化
        self.optimal_point = None  # 存储用户目标对应的最优解
        self.optimal_points = []  # 存储所有解点（解析解和数值解）
        self.user_target_solution = None  # 存储用户目标对应的最佳解（范围内的最大/最小值）
        self.analytical_failed = False  # 标记解析法是否失败
        self.current_method = None  # 当前使用的求解方法
        self.response_min = None  # 响应值最小值（在因子范围内）
        self.response_max = None  # 响应值最大值（在因子范围内）
        self.data_loaded = False  # 标记数据是否已加载

    def _on_objective_change(self):
        """当优化目标改变时触发"""
        self.optimization_type = self.objective_var.get()
        if self.data_loaded:  # 如果数据已加载，重新计算最优解
            factors = list(self.factors_info.keys()) if self.factors_info else []
            if factors:
                self.calculate_optimal_points(factors, self.method_var.get())
                self.optimize()
                # 重新绘制响应曲面图以更新标记
                if len(factors) >= 2:
                    self.visualize_response_surface(None, self.quadratic_model, factors, self.response)

    def _on_method_change(self):
        """当优化方法改变时触发"""
        if self.data_loaded:  # 如果数据已加载，重新计算最优解
            factors = list(self.factors_info.keys()) if self.factors_info else []
            if factors:
                self.calculate_optimal_points(factors, self.method_var.get())
                self.optimize()
                # 重新绘制响应曲面图以更新标记
                if len(factors) >= 2:
                    self.visualize_response_surface(None, self.quadratic_model, factors, self.response)

    def set_csv_path(self, path):
        self.csv_path = path
        print(f"收到路径: {self.csv_path}")

    def select_file(self):
        if self.csv_path:
            file_path = self.csv_path
        else:
            file_path = filedialog.askopenfilename(filetypes=[("CSV files", "*.csv"), ("Excel files", "*.xlsx;*.xls")])
        if file_path:
            self.file_path_var.set(file_path)
            self.analyze_button.config(state=tk.NORMAL)

    def perform_analysis(self):
        file_path = self.file_path_var.get()
        if not file_path:
            return

        try:
            # 清空之前的结果
            self.clear_results()
            self.analytical_failed = False  # 重置解析法失败标记
            self.current_method = None  # 重置当前方法
            self.response_min = None  # 重置响应值范围
            self.response_max = None
            self.data_loaded = False  # 重置数据加载标记

            if file_path.endswith('.csv'):
                df = pd.read_csv(file_path)
            elif file_path.endswith(('.xlsx', '.xls')):
                df = pd.read_excel(file_path)
            else:
                raise ValueError("不支持的文件格式，仅支持 CSV 和 Excel 文件。")

            # 清理列名
            df.columns = df.columns.str.strip().str.replace(':', '_').str.replace(' ', '_')
            df.columns = df.columns.str.strip()  # 去除首尾空格
            df.columns = [re.sub(r'[^\w\u4e00-\u9fa5]', '_', col) for col in df.columns]  # 替换特殊字符为下划线

            # 提取因子和响应变量
            factors = df.columns[:-1]
            self.response = df.columns[-1]  # 保存响应变量名

            # 存储因素的水平信息
            self.factors_info = {}
            for factor in factors:
                unique_values = sorted(df[factor].unique())
                if len(unique_values) >= 2:
                    # 记录因子的实际范围
                    self.factors_info[factor] = {
                        'low': unique_values[0],
                        'high': unique_values[-1],
                        'center': (unique_values[0] + unique_values[-1]) / 2,
                        'range': unique_values[-1] - unique_values[0]
                    }

            # 构建全因子模型公式（包含二次项）
            self.quadratic_formula = self._build_quadratic_formula(factors)

            # 拟合二次模型
            self.quadratic_model = ols(self.quadratic_formula, data=df).fit()

            # 创建符号模型
            self._create_symbolic_model(factors)

            # 使用用户选择的优化目标
            self.optimization_type = self.objective_var.get()

            # 计算R方和调整R方
            r_squared = self.quadratic_model.rsquared
            adj_r_squared = self.quadratic_model.rsquared_adj

            # 进行方差分析
            anova_table = sm.stats.anova_lm(self.quadratic_model, typ=2)

            # 主效应分析
            main_effects = {}
            for factor in factors:
                main_effects[factor] = self.quadratic_model.params[
                    factor] if factor in self.quadratic_model.params else 0

            # 交互效应分析
            interaction_effects = {}
            for term in self.quadratic_model.params.index:
                if ':' in term:
                    interaction_effects[term] = self.quadratic_model.params[term]

            # 二次项效应分析
            quadratic_effects = {}
            for factor in factors:
                quadratic_term = f'I({factor} ** 2)'
                if quadratic_term in self.quadratic_model.params.index:
                    quadratic_effects[quadratic_term] = self.quadratic_model.params[quadratic_term]

            # 筛选关键因子
            significant_factors = anova_table[anova_table['PR(>F)'] < 0.05].index.tolist()

            # 筛选显著的交互作用和二次项
            significant_interactions = [term for term in interaction_effects if term in significant_factors]
            significant_quadratics = [term for term in quadratic_effects if term in significant_factors]

            # 保存因子数据
            self.selected_factors_data = df[factors]

            # 计算效应大小
            effects = self.quadratic_model.params.drop('Intercept')

            # 计算最优解 - 根据选择的方法计算
            selected_method = self.method_var.get()
            self.calculate_optimal_points(factors, selected_method)

            # 记录当前使用的方法
            self.current_method = "解析法" if not self.analytical_failed else "数值法"

            # 计算整个因子空间的响应值范围
            self.calculate_response_range(factors)

            # 可视化结果
            self.visualize_goodness_of_fit(r_squared, adj_r_squared)
            self.visualize_effects(effects)

            # 响应曲面图
            if len(factors) >= 2:
                self.visualize_response_surface(df, self.quadratic_model, factors, self.response)

            # 输出分析结果
            result_str = "主效应分析结果:\n"
            for factor, effect in main_effects.items():
                result_str += f"{factor}: {effect:.4f}\n"

            result_str += "\n交互效应分析结果:\n"
            for term, effect in interaction_effects.items():
                significance = "*" if term in significant_factors else ""
                result_str += f"{term}: {effect:.4f} {significance}\n"

            result_str += "\n二次效应分析结果:\n"
            for term, effect in quadratic_effects.items():
                significance = "*" if term in significant_factors else ""
                result_str += f"{term}: {effect:.4f} {significance}\n"

            result_str += "\n\n模型拟合优度:\n"
            result_str += f"R方: {r_squared:.4f}\n"
            result_str += f"调整R方: {adj_r_squared:.4f}\n"
            result_str += f"样本量: {len(df)}\n"
            result_str += f"模型项数: {len(self.quadratic_model.params)}\n"

            result_str += "\n方差分析结果:\n"
            result_str += anova_table.to_csv(sep='\t', na_rep='nan')

            result_str += "\n显著因子(p<0.05):\n"
            result_str += ', '.join(significant_factors)

            # 添加优化目标信息
            result_str += f"\n\n用户选择的优化目标: {self.optimization_type}\n"

            # 添加求解方法信息
            if self.analytical_failed:
                result_str += "\n注意: 解析法未能找到有效解，已自动切换为数值法求解\n"
            else:
                result_str += f"\n使用{selected_method}求解得到有效解\n"

            # 添加响应值范围信息
            if self.response_min is not None and self.response_max is not None:
                result_str += f"\n因子范围内的响应值范围: [{self.response_min:.4f}, {self.response_max:.4f}]\n"

            # 显示结果
            self.summary_text.insert(tk.END, result_str)

            # 显示模型
            self.display_model_equation()

            # 启用优化按钮
            self.optimize_button.config(state=tk.NORMAL)

            # 标记数据已加载
            self.data_loaded = True

            # 分析完成后自动执行优化
            self.optimize()

        except Exception as e:
            self.summary_text.insert(tk.END, f"分析过程中出现错误: {str(e)}")
            import traceback
            traceback.print_exc()

    def calculate_response_range(self, factors):
        """计算因子范围内的响应值实际范围"""
        if not self.factors_info or not self.quadratic_model:
            return

        # 创建因子空间的采样点
        num_samples = 20  # 增加采样点以更准确地找到极值
        factor_ranges = []

        for factor in factors:
            # 获取因子的实际范围
            low = self.factors_info[factor]['low']
            high = self.factors_info[factor]['high']
            # 生成采样点（在实际实验数据范围内）
            factor_range = np.linspace(low, high, num_samples)
            factor_ranges.append(factor_range)

        # 创建网格
        meshes = np.meshgrid(*factor_ranges)
        grid_points = np.vstack([mesh.ravel() for mesh in meshes]).T

        # 创建数据框
        df = pd.DataFrame(grid_points, columns=factors)

        # 添加二次项和交互项
        for factor in factors:
            df[f'I({factor} ** 2)'] = df[factor] ** 2

        for i, f1 in enumerate(factors):
            for f2 in factors[i + 1:]:
                df[f'{f1}:{f2}'] = df[f1] * df[f2]

        # 使用模型预测所有点的响应值
        predictions = self.quadratic_model.predict(df)

        # 确定响应值范围（仅考虑因子范围内的值）
        self.response_min = np.min(predictions)
        self.response_max = np.max(predictions)

        print(f"因子范围内的响应值范围: [{self.response_min:.4f}, {self.response_max:.4f}]")

    def calculate_optimal_points(self, factors, method):
        """计算所有可能的解点，并确定用户目标对应的最优解"""
        self.optimal_points = []  # 重置解点列表
        self.analytical_failed = False  # 重置解析法失败标记
        self.optimal_point = None  # 重置最优解
        self.user_target_solution = None  # 重置用户目标解

        if not self.factors_info or not self.quadratic_model or not self.optimization_type:
            return

        # 根据选择的方法获取解点
        if method == "解析法":
            # 尝试解析法
            analytical_solutions = self._get_analytical_solutions(factors)

            # 检查解析解是否在因子范围内
            valid_analytical = [sol for sol in analytical_solutions if
                                all(self.factors_info[f]['low'] - 1e-6 <= v <= self.factors_info[f]['high'] + 1e-6
                                    for f, v in sol['factors'].items())]

            if valid_analytical:
                # 解析法成功，添加解析解
                for sol in valid_analytical:
                    self.optimal_points.append({
                        'type': '解析解',
                        'factors': sol['factors'],
                        'response': sol['response'],
                        'point_type': sol['point_type']
                    })
            else:
                # 解析法失败，使用数值法
                self.analytical_failed = True
                print("解析法未能找到有效解，将使用数值法求解")
                self._get_numerical_solution(factors)
        else:
            # 直接使用数值法
            self.analytical_failed = True
            self._get_numerical_solution(factors)

        # 计算用户目标对应的最优解（范围内的最大/最小值）
        self._calculate_user_target_solution(factors)

        # 从现有解点中选择最优解
        if self.optimal_points:
            # 筛选出在因子范围内的解点
            valid_points = [
                p for p in self.optimal_points
                if all(self.factors_info[f]['low'] - 1e-6 <= v <= self.factors_info[f]['high'] + 1e-6
                       for f, v in p['factors'].items())
            ]

            if valid_points:
                # 根据用户目标选择最优解（最大值或最小值）
                if self.optimization_type == "最大化":
                    self.optimal_point = max(valid_points, key=lambda x: x['response'])
                else:
                    self.optimal_point = min(valid_points, key=lambda x: x['response'])

    def _calculate_user_target_solution(self, factors):
        """计算用户目标对应的最佳解（在因子范围内的最大/最小值）"""
        if not self.factors_info or not self.quadratic_model:
            return

        # 使用数值优化方法在因子范围内寻找用户目标的最佳解
        def objective(x):
            # x是标准化的因子值（从-1到1）
            data = {}
            for i, factor in enumerate(factors):
                low = self.factors_info[factor]['low']
                high = self.factors_info[factor]['high']
                # 将标准化值转换为实际值
                actual_value = low + (x[i] + 1) * (high - low) / 2
                data[factor] = actual_value

            # 预测响应值
            prediction = self._predict_response(data)
            if prediction is None:
                return float('inf') if self.optimization_type == "最小化" else -float('inf')

            # 根据优化目标返回相应的值
            if self.optimization_type == "最大化":
                return -prediction  # 最大化问题转换为最小化
            else:
                return prediction  # 最小化直接返回

        # 约束条件：所有因子都在-1到1之间（确保在因子范围内）
        bounds = [(-1, 1) for _ in range(len(factors))]

        # 多初始点优化，提高找到全局最优的概率
        initial_guesses = [
            [0] * len(factors),  # 中心点
            [-1] * len(factors),  # 所有因子取最小值
            [1] * len(factors),  # 所有因子取最大值
            np.random.uniform(-1, 1, len(factors)),  # 随机点1
            np.random.uniform(-1, 1, len(factors))  # 随机点2
        ]

        best_result = None
        best_value = float('inf')

        for guess in initial_guesses:
            try:
                result = minimize(objective, guess, method='L-BFGS-B', bounds=bounds)
                if result.success and result.fun < best_value:
                    best_result = result
                    best_value = result.fun
            except:
                continue

        if best_result is not None:
            # 转换结果为实际值
            optimal_values = {}
            for i, factor in enumerate(factors):
                low = self.factors_info[factor]['low']
                high = self.factors_info[factor]['high']
                actual_value = low + (best_result.x[i] + 1) * (high - low) / 2
                optimal_values[factor] = actual_value

            # 预测响应值
            optimal_response = self._predict_response(optimal_values)

            if optimal_response is not None:
                self.user_target_solution = {
                    'type': '用户目标解',
                    'factors': optimal_values,
                    'response': optimal_response,
                    'target': self.optimization_type
                }

    def _get_analytical_solutions(self, factors):
        """获取解析解"""
        solutions = []
        if not self.sympy_model:
            return solutions

        try:
            # 构建偏导数方程组
            equations = []
            for factor in factors:
                partial_derivative = sp.diff(self.sympy_model, self.sympy_vars[factor])
                equations.append(partial_derivative)

            # 求解方程组
            solution = sp.solve(equations, list(self.sympy_vars.values()), dict=True)
            if not solution:
                return solutions

            # 计算Hessian矩阵以确定点类型
            hessian = sp.hessian(self.sympy_model, list(self.sympy_vars.values()))

            for sol in solution:
                # 计算该解点的Hessian矩阵
                hessian_at_point = hessian.subs(sol)

                # 确定点类型（极大值、极小值或鞍点）
                point_type = "未知"
                try:
                    eigenvals = hessian_at_point.eigenvals()
                    eigenvals_list = list(eigenvals.keys())

                    all_positive = all(eigenval > 0 for eigenval in eigenvals_list)
                    all_negative = all(eigenval < 0 for eigenval in eigenvals_list)

                    if all_positive:
                        point_type = "极小值点"
                    elif all_negative:
                        point_type = "极大值点"
                    else:
                        point_type = "鞍点"
                except:
                    point_type = "无法确定"

                # 转换解为实际值
                optimal_values = {}
                valid_solution = True
                for factor in factors:
                    sym_factor = self.sympy_vars[factor]
                    value = sol.get(sym_factor, None)
                    if value is None:
                        valid_solution = False
                        break

                    # 尝试将符号表达式转换为浮点数
                    try:
                        float_value = float(value)
                    except:
                        valid_solution = False
                        break

                    optimal_values[factor] = float_value

                if not valid_solution:
                    continue

                # 预测响应值
                optimal_response = self._predict_response(optimal_values)
                if optimal_response is not None:
                    solutions.append({
                        'factors': optimal_values,
                        'response': optimal_response,
                        'point_type': point_type
                    })

        except Exception as e:
            print(f"解析解计算错误: {str(e)}")

        return solutions

    def _get_numerical_solution(self, factors):
        """获取数值解"""
        try:
            # 创建目标函数
            def objective(x):
                # x是标准化的因子值（从-1到1）
                data = {}
                for i, factor in enumerate(factors):
                    low = self.factors_info[factor]['low']
                    high = self.factors_info[factor]['high']
                    # 将标准化值转换为实际值
                    actual_value = low + (x[i] + 1) * (high - low) / 2
                    data[factor] = actual_value

                # 预测响应值
                prediction = self._predict_response(data)
                if prediction is None:
                    return float('inf') if self.optimization_type == "最小化" else -float('inf')

                # 根据优化目标返回相应的值
                if self.optimization_type == "最大化":
                    return -prediction  # 最大化问题转换为最小化
                else:
                    return prediction  # 最小化直接返回

            # 约束条件：所有因子都在-1到1之间（确保在因子范围内）
            bounds = [(-1, 1) for _ in range(len(factors))]

            # 多初始点优化，提高找到全局最优的概率
            initial_guesses = [
                [0] * len(factors),  # 中心点
                [-1] * len(factors),  # 所有因子取最小值
                [1] * len(factors)  # 所有因子取最大值
            ]

            # 存储所有找到的解点
            found_points = []

            for guess in initial_guesses:
                result = minimize(objective, guess, method='L-BFGS-B', bounds=bounds)
                if result.success:
                    # 转换结果为实际值
                    optimal_values = {}
                    for i, factor in enumerate(factors):
                        low = self.factors_info[factor]['low']
                        high = self.factors_info[factor]['high']
                        actual_value = low + (result.x[i] + 1) * (high - low) / 2
                        optimal_values[factor] = actual_value

                    # 预测响应值
                    optimal_response = self._predict_response(optimal_values)

                    if optimal_response is not None:
                        # 检查是否已存在相似解点
                        duplicate = False
                        for p in found_points:
                            if all(abs(optimal_values[f] - p['factors'][f]) < 1e-6 for f in factors):
                                duplicate = True
                                break
                        if not duplicate:
                            found_points.append({
                                'type': '数值解',
                                'factors': optimal_values,
                                'response': optimal_response,
                                'status': result.message
                            })

            # 添加所有找到的数值解
            self.optimal_points.extend(found_points)

        except Exception as e:
            print(f"数值解计算错误: {str(e)}")

    def _predict_response(self, factor_values):
        """统一的响应值预测方法"""
        if not self.quadratic_model:
            return None

        try:
            # 创建DataFrame
            df = pd.DataFrame([factor_values])

            # 添加二次项
            for factor in factor_values.keys():
                df[f'I({factor} ** 2)'] = df[factor] ** 2

            # 添加交互项
            factors = list(factor_values.keys())
            for i, f1 in enumerate(factors):
                for f2 in factors[i + 1:]:
                    df[f'{f1}:{f2}'] = df[f1] * df[f2]

            # 使用模型预测
            return self.quadratic_model.predict(df).iloc[0]
        except Exception as e:
            print(f"响应值预测错误: {str(e)}")
            return None

    def _build_quadratic_formula(self, factors):
        """构建包含二次项的模型公式"""
        # 主效应
        main_effects = ' + '.join(factors)

        # 交互效应
        interactions = []
        for i, f1 in enumerate(factors):
            for f2 in factors[i + 1:]:
                interactions.append(f'{f1}:{f2}')
        interaction_terms = ' + '.join(interactions) if interactions else ''

        # 二次效应
        quadratics = [f'I({f} ** 2)' for f in factors]
        quadratic_terms = ' + '.join(quadratics)

        # 组合所有项
        all_terms = [main_effects, interaction_terms, quadratic_terms]
        all_terms = [t for t in all_terms if t]  # 移除空字符串
        formula = f'{self.response} ~ ' + ' + '.join(all_terms)

        return formula

    def _create_symbolic_model(self, factors):
        """创建符号模型"""
        # 创建符号变量
        self.sympy_vars = {factor: sp.Symbol(factor) for factor in factors}

        # 初始化符号表达式
        expr = sp.sympify(float(self.quadratic_model.params['Intercept']))

        # 添加主效应项
        for factor in factors:
            if factor in self.quadratic_model.params:
                expr += float(self.quadratic_model.params[factor]) * self.sympy_vars[factor]

        # 添加交互项
        for term in self.quadratic_model.params.index:
            if ':' in term:
                factors_in_term = term.split(':')
                if all(f in self.sympy_vars for f in factors_in_term):
                    product = 1
                    for f in factors_in_term:
                        product *= self.sympy_vars[f]
                    expr += float(self.quadratic_model.params[term]) * product

        # 添加二次项
        for factor in factors:
            quadratic_term = f'I({factor} ** 2)'
            if quadratic_term in self.quadratic_model.params:
                expr += float(self.quadratic_model.params[quadratic_term]) * self.sympy_vars[factor] ** 2

        # 保存符号模型
        self.sympy_model = expr

    def display_model_equation(self):
        """显示模型方程"""
        if self.quadratic_model is None:
            return

        # 获取模型参数
        params = self.quadratic_model.params

        # 构建模型方程字符串
        model_str = f"{self.response} = {params['Intercept']:.4f}"

        # 添加主效应项
        for factor in self.factors_info.keys():
            if factor in params:
                model_str += f" + {params[factor]:.4f}*{factor}"

        # 添加交互项
        for term in params.index:
            if ':' in term:
                factors_in_term = term.split(':')
                if all(f in self.factors_info for f in factors_in_term):
                    model_str += f" + {params[term]:.4f}*{term.replace(':', '*')}"

        # 添加二次项
        for factor in self.factors_info.keys():
            quadratic_term = f'I({factor} ** 2)'
            if quadratic_term in params:
                model_str += f" + {params[quadratic_term]:.4f}*{factor}²"

        # 显示模型方程
        self.model_text.insert(tk.END, "模型方程:\n\n")
        self.model_text.insert(tk.END, model_str + "\n\n")

        # 显示用户选择的优化目标
        self.model_text.insert(tk.END, f"用户选择的优化目标: {self.optimization_type}\n\n")

        # 显示求解方法信息
        if self.analytical_failed:
            self.model_text.insert(tk.END, "求解方法: 使用数值法求解\n\n")
        else:
            self.model_text.insert(tk.END, f"求解方法: 使用{self.method_var.get()}求解\n\n")

        # 显示响应值范围
        if self.response_min is not None and self.response_max is not None:
            self.model_text.insert(tk.END,
                                   f"因子范围内的响应值范围: [{self.response_min:.4f}, {self.response_max:.4f}]\n\n")

        # 显示模型摘要
        self.model_text.insert(tk.END, "模型参数详情:\n\n")
        for param, value in params.items():
            self.model_text.insert(tk.END, f"{param}: {value:.6f}\n")

        self.model_text.insert(tk.END, "\n模型摘要:\n\n")
        self.model_text.insert(tk.END, self.quadratic_model.summary().as_text())

    def clear_results(self):
        # 清空文本框
        self.summary_text.delete(1.0, tk.END)
        self.gradient_text.delete(1.0, tk.END)
        self.model_text.delete(1.0, tk.END)

        # 清除图表
        if self.goodness_of_fit_fig:
            for widget in self.summary_tab.winfo_children():
                if isinstance(widget, FigureCanvasTkAgg):
                    widget.get_tk_widget().destroy()
            self.goodness_of_fit_fig = None

        if self.effects_fig:
            for widget in self.effects_tab.winfo_children():
                if isinstance(widget, FigureCanvasTkAgg):
                    widget.get_tk_widget().destroy()
            self.effects_fig = None

        if self.surface_fig:
            for widget in self.surface_tab.winfo_children():
                if isinstance(widget, FigureCanvasTkAgg):
                    widget.get_tk_widget().destroy()
            self.surface_fig = None

        if self.optimization_fig:
            for widget in self.gradient_tab.winfo_children():
                if isinstance(widget, FigureCanvasTkAgg):
                    widget.get_tk_widget().destroy()
            self.optimization_fig = None

        # 重置变量
        self.optimal_point = None
        self.optimal_points = []
        self.user_target_solution = None
        self.response_min = None
        self.response_max = None

    def visualize_goodness_of_fit(self, r_squared, adj_r_squared):
        # 创建拟合优度图表
        self.goodness_of_fit_fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))

        # R方饼图
        explained = r_squared
        unexplained = 1 - r_squared
        labels = ['已解释变异', '未解释变异']
        colors = ['#4CAF50', '#F44336']
        explode = (0.1, 0)

        ax1.pie([explained, unexplained], explode=explode, labels=labels, colors=colors,
                autopct='%1.2f%%', shadow=True, startangle=90)
        ax1.set_title(f'R方 = {r_squared:.4f}')
        ax1.axis('equal')

        # R方和调整R方对比图
        metrics = ['R方', '调整R方']
        values = [r_squared, adj_r_squared]
        x_pos = np.arange(len(metrics))

        ax2.set_ylim(0, 1.1)
        bars = ax2.bar(x_pos, values, align='center', alpha=0.7, color=['#2196F3', '#9C27B0'])
        ax2.set_xticks(x_pos)
        ax2.set_xticklabels(metrics)
        ax2.set_ylabel('值')
        ax2.set_title('R方与调整R方对比')

        # 添加数值标签
        for bar in bars:
            height = bar.get_height()
            if height > 0.9:
                ax2.text(bar.get_x() + bar.get_width() / 2., height - 0.05,
                         f'{height:.4f}', ha='center', va='bottom', color='white')
            else:
                ax2.text(bar.get_x() + bar.get_width() / 2., height + 0.02,
                         f'{height:.4f}', ha='center', va='bottom')

        plt.tight_layout()

        # 将图表添加到摘要标签页
        canvas = FigureCanvasTkAgg(self.goodness_of_fit_fig, master=self.summary_tab)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=5)

    def visualize_effects(self, effects):
        # 创建效应分析图表
        self.effects_fig, ax = plt.subplots(figsize=(10, 5))

        # Pareto图
        effects_abs = effects.abs().sort_values(ascending=False)
        total_effect = effects_abs.sum()
        cumulative_percentage = effects_abs.cumsum() / total_effect * 100

        ax.bar(effects_abs.index, effects_abs.values, color='b')
        ax.set_xlabel('因子效应')
        ax.set_ylabel('效应绝对值', color='b')
        ax.tick_params(axis='y', labelcolor='b')
        plt.xticks(rotation=90)

        ax_twin = ax.twinx()
        ax_twin.plot(effects_abs.index, cumulative_percentage.values, color='r', marker='o')
        ax_twin.set_ylabel('累积百分比(%)', color='r')
        ax_twin.tick_params(axis='y', labelcolor='r')

        ax.set_title('因子效应的Pareto图')

        plt.tight_layout()

        # 将图表添加到效应标签页
        canvas = FigureCanvasTkAgg(self.effects_fig, master=self.effects_tab)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=5)

    def visualize_response_surface(self, df, model, factors, response):
        if len(factors) < 2 or self.response_min is None or self.response_max is None:
            return

        # 清除之前的响应曲面图
        for widget in self.surface_tab.winfo_children():
            if isinstance(widget, FigureCanvasTkAgg):
                widget.get_tk_widget().destroy()

        # 创建响应曲面图
        self.surface_fig = plt.figure(figsize=(10, 8))

        try:
            if len(factors) == 2:  # 两因子情况
                factor1 = factors[0]
                factor2 = factors[1]

                # 获取因子范围
                min_factor1 = self.factors_info[factor1]['low']
                max_factor1 = self.factors_info[factor1]['high']
                min_factor2 = self.factors_info[factor2]['low']
                max_factor2 = self.factors_info[factor2]['high']

                # 创建网格点
                factor1_range = np.linspace(min_factor1, max_factor1, 150)
                factor2_range = np.linspace(min_factor2, max_factor2, 150)
                factor1_grid, factor2_grid = np.meshgrid(factor1_range, factor2_range)

                data_grid = pd.DataFrame({
                    factor1: factor1_grid.flatten(),
                    factor2: factor2_grid.flatten()
                })
                for factor in factors[2:]:
                    data_grid[factor] = self.factors_info[factor]['center']  # 使用中心点

                # 添加二次项和交互项
                for factor in [factor1, factor2]:
                    data_grid[f'I({factor} ** 2)'] = data_grid[factor] ** 2
                data_grid[f'{factor1}:{factor2}'] = data_grid[factor1] * data_grid[factor2]

                # 使用模型预测响应值
                predictions = model.predict(data_grid)
                prediction_grid = predictions.values.reshape(factor1_grid.shape)

                # 创建3D坐标轴
                ax = self.surface_fig.add_subplot(111, projection='3d')

                # 绘制响应曲面
                surf = ax.plot_surface(
                    factor1_grid,
                    factor2_grid,
                    prediction_grid,
                    cmap='viridis',
                    alpha=0.8,
                    vmin=self.response_min,
                    vmax=self.response_max
                )

                # 设置坐标轴范围
                ax.set_xlim(min_factor1, max_factor1)
                ax.set_ylim(min_factor2, max_factor2)
                ax.set_zlim(self.response_min, self.response_max)

                # 设置坐标轴标签
                ax.set_xlabel(f'{factor1}')
                ax.set_ylabel(f'{factor2}')
                ax.set_zlabel(f'{response}', rotation=90)

                # 设置标题
                ax.set_title(f'{factor1} 和 {factor2} 的响应曲面图 (目标: {self.optimization_type})', pad=20)

                # 添加颜色条
                cbar = self.surface_fig.colorbar(surf, ax=ax, shrink=0.5, aspect=10, pad=0.1)
                cbar.set_label(f'{response}')

                # 设置颜色条刻度
                num_ticks = 5
                tick_values = np.linspace(self.response_min, self.response_max, num_ticks)
                cbar.set_ticks(tick_values)
                cbar.set_ticklabels([f'{v:.2f}' for v in tick_values])

                # 标记解点
                self._mark_solution_points(ax, factors, factor1, factor2, min_factor1, max_factor1, min_factor2,
                                           max_factor2)

                # 设置视角
                ax.view_init(elev=30, azim=45)

            elif len(factors) == 3:  # 三因子情况
                factor1 = factors[0]
                factor2 = factors[1]
                factor3 = factors[2]

                # 获取各因子范围
                min_f1, max_f1 = self.factors_info[factor1]['low'], self.factors_info[factor1]['high']
                min_f2, max_f2 = self.factors_info[factor2]['low'], self.factors_info[factor2]['high']
                min_f3, max_f3 = self.factors_info[factor3]['low'], self.factors_info[factor3]['high']

                # 创建三维散点图
                ax = self.surface_fig.add_subplot(111, projection='3d')

                # 创建网格点
                x = np.linspace(min_f1, max_f1, 25)
                y = np.linspace(min_f2, max_f2, 25)
                z = np.linspace(min_f3, max_f3, 25)

                # 创建网格
                X, Y, Z = np.meshgrid(x, y, z)

                # 为网格中的每个点创建数据框
                grid_data = pd.DataFrame({
                    factor1: X.flatten(),
                    factor2: Y.flatten(),
                    factor3: Z.flatten()
                })

                # 为其他因子设置中心点
                for factor in factors[3:]:
                    grid_data[factor] = self.factors_info[factor]['center']

                # 添加二次项和交互项
                for factor in [factor1, factor2, factor3]:
                    grid_data[f'I({factor} ** 2)'] = grid_data[factor] ** 2
                grid_data[f'{factor1}:{factor2}'] = grid_data[factor1] * grid_data[factor2]
                grid_data[f'{factor1}:{factor3}'] = grid_data[factor1] * grid_data[factor3]
                grid_data[f'{factor2}:{factor3}'] = grid_data[factor2] * grid_data[factor3]

                # 使用模型预测每个点的响应值
                predictions = model.predict(grid_data)

                # 设置坐标轴范围
                ax.set_xlim(min_f1, max_f1)
                ax.set_ylim(min_f2, max_f2)
                ax.set_zlim(min_f3, max_f3)

                # 使用颜色深浅表示响应值，从浅到深对应从小到大
                scatter = ax.scatter(
                    X.flatten(),
                    Y.flatten(),
                    Z.flatten(),
                    c=predictions,
                    cmap='viridis',
                    alpha=0.7,
                    s=40,
                    edgecolor='none',
                    vmin=self.response_min,
                    vmax=self.response_max
                )

                # 设置坐标轴标签
                ax.set_xlabel(f'{factor1}')
                ax.set_ylabel(f'{factor2}')
                ax.set_zlabel(f'{factor3}')

                # 设置标题，强调颜色与响应值的对应关系
                ax.set_title(
                    f'{factor1}, {factor2} 和 {factor3} 的响应分布\n(颜色从深到浅对应响应值从小到大，目标: {self.optimization_type})',
                    pad=20)

                # 添加颜色条
                cbar = self.surface_fig.colorbar(scatter, ax=ax, shrink=0.5, aspect=15, pad=0.1)
                cbar.set_label(f'{response} (深色→浅色 对应 小值→大值)', rotation=90, labelpad=15)

                # 设置颜色条刻度
                num_ticks = 7
                tick_values = np.linspace(self.response_min, self.response_max, num_ticks)
                cbar.set_ticks(tick_values)
                cbar.set_ticklabels([f'{v:.2f}' for v in tick_values])

                # 标记解点
                self._mark_solution_points_3d(ax, factors, factor1, factor2, factor3, min_f1, max_f1, min_f2, max_f2,
                                              min_f3, max_f3)

                # 调整视角
                ax.view_init(elev=25, azim=45)

            else:
                # 超过三个因子的情况
                ax = self.surface_fig.add_subplot(111)
                ax.text(0.5, 0.5, f"响应曲面图仅支持2-3个因子\n当前因子数量: {len(factors)}",
                        ha='center', va='center', fontsize=12)
                ax.axis('off')

            plt.tight_layout()
        except Exception as e:
            # 如果生成曲面图失败，显示错误信息
            ax = self.surface_fig.add_subplot(111)
            ax.text(0.5, 0.5, f"无法生成响应曲面图\n错误: {str(e)}", ha='center', va='center', fontsize=12)
            ax.axis('off')
            import traceback
            traceback.print_exc()

        # 将图表添加到响应曲面标签页
        canvas = FigureCanvasTkAgg(self.surface_fig, master=self.surface_tab)
        canvas.draw()
        canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, pady=5)

    def _mark_solution_points(self, ax, factors, factor1, factor2, min_factor1, max_factor1, min_factor2, max_factor2):
        """在2D响应曲面图上标记解点"""
        if not self.optimal_points:
            return

        colors = {'解析解': 'red', '数值解': 'blue', '用户目标解': 'orange'}
        markers = {'解析解': 'o', '数值解': 'D', '用户目标解': '*'}

        # 标记所有解点
        for opt_point in self.optimal_points:
            # 只显示当前方法对应的解点
            if (not self.analytical_failed and opt_point['type'] == '解析解') or \
                    (self.analytical_failed and opt_point['type'] == '数值解'):

                opt_x = opt_point['factors'][factor1]
                opt_y = opt_point['factors'][factor2]
                opt_z = opt_point['response']

                # 确保解点在绘图范围内
                if min_factor1 <= opt_x <= max_factor1 and min_factor2 <= opt_y <= max_factor2:
                    ax.scatter([opt_x], [opt_y], [opt_z],
                               color=colors[opt_point['type']],
                               s=150,
                               marker=markers[opt_point['type']],
                               label=f"{opt_point['type']}")

                    # 添加投影线
                    ax.plot([opt_x, opt_x], [opt_y, opt_y], [self.response_min, opt_z], 'k--', linewidth=1)
                    ax.plot([opt_x, opt_x], [min_factor2, opt_y], [opt_z, opt_z], 'k--', linewidth=1)
                    ax.plot([min_factor1, opt_x], [opt_y, opt_y], [opt_z, opt_z], 'k--', linewidth=1)

                    # 添加标签
                    ax.text(opt_x, opt_y, opt_z,
                            f'{opt_point["type"]}\n({opt_x:.2f}, {opt_y:.2f}, {opt_z:.2f})',
                            color=colors[opt_point['type']],
                            fontsize=9, ha='center', va='bottom')

        # 特殊标记用户目标解（星号）
        if self.user_target_solution:
            opt_x = self.user_target_solution['factors'][factor1]
            opt_y = self.user_target_solution['factors'][factor2]
            opt_z = self.user_target_solution['response']

            if min_factor1 <= opt_x <= max_factor1 and min_factor2 <= opt_y <= max_factor2:
                ax.scatter([opt_x], [opt_y], [opt_z],
                           color='orange',
                           s=300,
                           marker='*',
                           label=f"用户目标{self.optimization_type}解",
                           edgecolor='black',
                           linewidth=2)

                # 添加投影线
                ax.plot([opt_x, opt_x], [opt_y, opt_y], [self.response_min, opt_z], 'orange', linewidth=2)
                ax.plot([opt_x, opt_x], [min_factor2, opt_y], [opt_z, opt_z], 'orange', linewidth=2)
                ax.plot([min_factor1, opt_x], [opt_y, opt_y], [opt_z, opt_z], 'orange', linewidth=2)

                # 添加标签
                ax.text(opt_x, opt_y, opt_z,
                        f'用户目标{self.optimization_type}解:\n({opt_x:.2f}, {opt_y:.2f}, {opt_z:.2f})',
                        color='orange',
                        fontsize=10, ha='center', va='bottom',
                        weight='bold',
                        bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))

        # 添加图例
        ax.legend()

    def _mark_solution_points_3d(self, ax, factors, factor1, factor2, factor3, min_f1, max_f1, min_f2, max_f2, min_f3,
                                 max_f3):
        """在3D响应分布图上标记解点"""
        if not self.optimal_points:
            return

        colors = {'解析解': 'red', '数值解': 'blue', '用户目标解': 'orange'}
        markers = {'解析解': 'o', '数值解': 'D', '用户目标解': '*'}

        # 标记所有解点
        for opt_point in self.optimal_points:
            # 只显示当前方法对应的解点
            if (not self.analytical_failed and opt_point['type'] == '解析解') or \
                    (self.analytical_failed and opt_point['type'] == '数值解'):

                if all(f in opt_point['factors'] for f in [factor1, factor2, factor3]):
                    opt_x = opt_point['factors'][factor1]
                    opt_y = opt_point['factors'][factor2]
                    opt_z = opt_point['factors'][factor3]
                    opt_response = opt_point['response']

                    # 确保解点在绘图范围内
                    if (min_f1 <= opt_x <= max_f1 and
                            min_f2 <= opt_y <= max_f2 and
                            min_f3 <= opt_z <= max_f3):
                        ax.scatter(
                            [opt_x], [opt_y], [opt_z],
                            color=colors[opt_point['type']],
                            s=200,
                            marker=markers[opt_point['type']],
                            label=f"{opt_point['type']}",
                            edgecolor='black',
                            linewidth=1
                        )

                        # 添加投影线
                        ax.plot([opt_x, opt_x], [opt_y, opt_y], [min_f3, opt_z], 'k--', linewidth=1)
                        ax.plot([opt_x, opt_x], [min_f2, opt_y], [opt_z, opt_z], 'k--', linewidth=1)
                        ax.plot([min_f1, opt_x], [opt_y, opt_y], [opt_z, opt_z], 'k--', linewidth=1)

                        # 添加标签
                        ax.text(opt_x, opt_y, opt_z,
                                f'{opt_point["type"]}\n({opt_x:.2f}, {opt_y:.2f}, {opt_z:.2f})\n响应: {opt_response:.2f}',
                                color=colors[opt_point['type']],
                                fontsize=9, ha='center', va='bottom',
                                bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))

        # 特殊标记用户目标解（星号）
        if self.user_target_solution:
            if all(f in self.user_target_solution['factors'] for f in [factor1, factor2, factor3]):
                opt_x = self.user_target_solution['factors'][factor1]
                opt_y = self.user_target_solution['factors'][factor2]
                opt_z = self.user_target_solution['factors'][factor3]
                opt_response = self.user_target_solution['response']

                if (min_f1 <= opt_x <= max_f1 and
                        min_f2 <= opt_y <= max_f2 and
                        min_f3 <= opt_z <= max_f3):
                    ax.scatter(
                        [opt_x], [opt_y], [opt_z],
                        color='orange',
                        s=300,
                        marker='*',
                        label=f"用户目标{self.optimization_type}解",
                        edgecolor='black',
                        linewidth=2
                    )

                    # 添加投影线
                    ax.plot([opt_x, opt_x], [opt_y, opt_y], [min_f3, opt_z], 'orange', linewidth=2)
                    ax.plot([opt_x, opt_x], [min_f2, opt_y], [opt_z, opt_z], 'orange', linewidth=2)
                    ax.plot([min_f1, opt_x], [opt_y, opt_y], [opt_z, opt_z], 'orange', linewidth=2)

                    # 添加标签
                    ax.text(opt_x, opt_y, opt_z,
                            f'用户目标{self.optimization_type}解:\n({opt_x:.2f}, {opt_y:.2f}, {opt_z:.2f})\n响应: {opt_response:.2f}',
                            color='orange',
                            fontsize=10, ha='center', va='bottom',
                            weight='bold',
                            bbox=dict(boxstyle='round,pad=0.3', facecolor='white', alpha=0.8))

        # 添加图例
        ax.legend()

    def optimize(self):
        """执行优化，显示结果"""
        if self.optimization_type is None:
            messagebox.showerror("错误", "请选择优化目标")
            return

        factors = list(self.factors_info.keys()) if self.factors_info else []

        # 清空之前的结果
        self.gradient_text.delete(1.0, tk.END)

        if not factors:
            self.gradient_text.insert(tk.END, "没有有效的因子信息，无法进行优化。")
            return

        # 显示使用的求解方法和响应值范围
        current_method = "解析法" if not self.analytical_failed else "数值法"
        result_str = f"{self.optimization_type}优化结果 (使用{current_method}):\n\n"

        # 添加响应值范围信息
        if self.response_min is not None and self.response_max is not None:
            result_str += f"因子范围内的响应值范围: [{self.response_min:.4f}, {self.response_max:.4f}]\n\n"

        # 首先显示用户目标对应的最佳解（范围内的最大/最小值）
        if self.user_target_solution:
            result_str += "=" * 60 + "\n"
            result_str += f"用户目标{self.optimization_type}解（范围内的{self.optimization_type}响应值）★:\n"
            result_str += "=" * 60 + "\n"
            result_str += f"最佳响应值: {self.user_target_solution['response']:.6f}\n"

            # 显示在整体范围中的相对位置
            if self.response_min is not None and self.response_max is not None and self.response_max > self.response_min:
                percentage = (self.user_target_solution['response'] - self.response_min) / (
                        self.response_max - self.response_min) * 100
                result_str += f"相对位置: {percentage:.1f}% (在因子范围内)\n"

            result_str += "最佳因子组合:\n"
            for factor, value in self.user_target_solution['factors'].items():
                result_str += f"  {factor}: {value:.6f}\n"
            result_str += "\n"

        # 然后展示所有找到的极值解点
        if self.optimal_points:
            result_str += "=" * 60 + "\n"
            result_str += "所有找到的极值解点:\n"
            result_str += "=" * 60 + "\n"

            displayed = False
            for i, opt_point in enumerate(self.optimal_points, 1):
                # 只显示当前方法对应的解点
                if (not self.analytical_failed and opt_point['type'] == '解析解') or \
                        (self.analytical_failed and opt_point['type'] == '数值解'):

                    displayed = True
                    result_str += f"解点 {i} ({opt_point['type']}):\n"
                    result_str += f"响应值: {opt_point['response']:.6f}\n"

                    # 显示该解点在整体范围中的相对位置
                    if self.response_min is not None and self.response_max is not None and self.response_max > self.response_min:
                        percentage = (opt_point['response'] - self.response_min) / (
                                self.response_max - self.response_min) * 100
                        result_str += f"相对位置: {percentage:.1f}% (在因子范围内)\n"

                    if opt_point['type'] == '解析解':
                        result_str += f"点类型: {opt_point['point_type']}\n"
                    else:
                        result_str += f"优化状态: {opt_point['status']}\n"

                    result_str += "因子组合:\n"
                    for factor, value in opt_point['factors'].items():
                        result_str += f"  {factor}: {value:.6f}\n"
                    result_str += "\n"

            if not displayed:
                result_str += "未找到当前方法对应的有效解点。\n\n"
        else:
            result_str += "未找到有效的极值解点。\n\n"

        # 添加解释说明
        result_str += "=" * 60 + "\n"
        result_str += "说明:\n"
        result_str += "=" * 60 + "\n"
        result_str += "★ 用户目标解: 在因子范围内，根据用户选择的优化目标（最大化或最小化）\n"
        result_str += "   找到的最佳响应值及其对应的因子组合。这是实际应用中的推荐解。\n\n"
        result_str += "• 极值解点: 通过求解偏导数方程组（解析法）或数值优化（数值法）\n"
        result_str += "   找到的数学极值点，包括极大值点、极小值点和鞍点。\n\n"
        result_str += "• 在响应曲面图中，用户目标解用橙色星号(★)标记，\n"
        result_str += "  极值解点用红色圆点(解析解)或蓝色菱形(数值解)标记。\n"

        # 显示结果
        self.gradient_text.insert(tk.END, result_str)


if __name__ == "__main__":
    root = tk.Tk()
    root.title("实验设计(DOE)分析工具")
    root.geometry("1000x700")
    root.minsize(800, 600)

    # 确保中文显示正常
    try:
        root.option_add("*Font", "SimHei 10")
    except:
        pass

    app = DOEAnalyzer(root)
    root.mainloop()